/*
 * ENUM Discoverer. ENUM Demonstration software, demonstrating the potential of ENUM.
 * 
 * Copyright (C) 2009 SIDN and ISOC.nl
 * 
 * This file is part of ENUM Discoverer.
 *
 * ENUM Discoverer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ENUM Discoverer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with ENUM Discoverer.  If not, see <http://www.gnu.org/licenses/>.
 */

package nl.sidn.enumdiscoverer.contacts;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.util.Log;

import nl.sidn.enumdiscoverer.LogConfig;
import nl.sidn.enumdiscoverer.Utils;
import nl.sidn.enumdiscoverer.contacts.adapters.ContactAdapter;

/**
 * A rule for mapping ENUM records to a Contact field by its service and URL.
 * @author	Grant Patterson (grant.patterson@catalyst.net.nz)
 */
public class ContactMappingRule {

	static final String TAG = LogConfig.getLogTag(ContactMappingRule.class);
	// whether DEBUG level logging is enabled (whether globally, or explicitly for this log tag)
	static final boolean DEBUG = LogConfig.isDebug(TAG);
	// whether VERBOSE level logging is enabled
	static final boolean VERBOSE = LogConfig.VERBOSE;
	
	
	// class invariants:
	// type not null
	// urlPattern not null
	// contactUri not null
	
	private final String mType;
	private final String mSubtype;
	private final Pattern mUrlPattern;
	private final ContactAdapter mContactAdapter;
	private final boolean mHasCapturingGroups;
	
	// precondition: all parameters are non-null
	public ContactMappingRule(String enumService, String urlPattern, ContactAdapter contactAdapter) {
		
		Utils.assertParamsNotNull(enumService, urlPattern, contactAdapter);
		
		// read the service type and subtype from enumService
		String[] serviceFields = enumService.split(":", 2);
		mType = serviceFields[0];
		mSubtype = serviceFields.length == 2 ? serviceFields[1] : null;
		
		mUrlPattern = Pattern.compile(urlPattern);
		mContactAdapter = contactAdapter;
		
		// determing whether urlPattern has any capturing groups
		int groupCount = mUrlPattern.matcher("").groupCount();
		mHasCapturingGroups = (groupCount > 0);
	}
	
	/**
	 * 
	 * @param service
	 * @param url
	 * @return The MappingResult of service and URL determined by this rule.
	 *         null if service and URL don't match this rule.
	 */
	public MappingResult doMapping(String service, String url) {
		
		// Check this is the service we were expecting
		if (!serviceMatches(service)) {
			//if (VERBOSE) Log.v(TAG, "Service " + service + " doesn't match " + getEnumService() + ". Rejecting");
			return null; 
		}
		
		// Check the url matches mUrlPattern
		Matcher m = mUrlPattern.matcher(url);
		if (!m.matches()) {
			//if (VERBOSE) Log.v(TAG, "URL " + url + " doesn't match " + mUrlPattern + ". Rejecting");
			return null;
		}
		
		// success.
		
		// If mUrlPattern had a capturing group, include it's match in the result.
		// Otherwise, use the whole URL
		String match = mHasCapturingGroups ? m.group(1) : url;
		return new MappingResult(mContactAdapter, match);
	}
	
	public String getType() {
		return mType;
	}
	
	public String getEnumService() {
		return mSubtype == null ? mType : mType + ":" + mSubtype;
	}
	
	public ContactAdapter getEnumContactAdapter() {
		return mContactAdapter;
	}
	
	public String toString() {
		return "{" + getEnumService() + ", " + mUrlPattern + ", " + mContactAdapter + "}";
	}
	
	private boolean serviceMatches(String service) {
		
		if (service == null)
			return false;
		
		String[] serviceFields = service.split(":", 2);
		
		if (!mType.equals(serviceFields[0])) {
			// types don't match
			return false;
		} else if (mSubtype == null) {
			// no subtype required. Match regardless of whether one is given
			return true;
		} else if (serviceFields.length == 2) {
			// matches if the subtypes match
			return mSubtype.equals(serviceFields[1]);
		} else {
			// mapping requires a subtype, but none have been given
			return false;
		}
	}
	
}
